Micron Document
🎖️GitЯра🎖️


Displaying Raw • Download

core/takserver/src/commonMain/kotlin/org/meshtastic/core/takserver/TAKPacketV2Conversion.kt fix/qr-error-correction (7b45f84f) Text, 13.13 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tf0883e@fileTb4b4b4:Te6edf3SuppressTb4b4b4(Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffMagicNumberTa5d6ff"Tb4b4b4)

Tff7b72package T7ee787org.meshtastic.core.takserver

Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787okio.ByteString.Companion.toByteString
Tff7b72import T7ee787org.meshtastic.proto.CotHow
Tff7b72import T7ee787org.meshtastic.proto.CotType
Tff7b72import T7ee787org.meshtastic.proto.GeoChat
Tff7b72import T7ee787org.meshtastic.proto.GeoPointSource
Tff7b72import T7ee787org.meshtastic.proto.MemberRole
Tff7b72import T7ee787org.meshtastic.proto.TAKPacketV2
Tff7b72import T7ee787org.meshtastic.proto.Team
Tff7b72import T7ee787kotlin.random.Random
Tff7b72import T7ee787kotlin.time.Clock
Tff7b72import T7ee787kotlin.time.Duration.Companion.minutes
Tff7b72import T7ee787kotlin.time.Duration.Companion.seconds

T8b949e/** Conversion between CoTMessage and TAKPacketV2 (v2 wire protocol). */
Tff7b72object T56d364TAKPacketV2Conversion Tb4b4b4{

Tff7b72fun Te6edf3CoTMessageTb4b4b4.Td2a8fftoTAKPacketV2Tb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3TAKPacketV2? Tb4b4b4{
Tff7b72val Te6edf3cotTypeEnum Tff7b72= Te6edf3TakV2TypeMapperTb4b4b4.Te6edf3cotTypeFromStringTb4b4b4(Te6edf3typeTb4b4b4)
Tff7b72val Te6edf3cotTypeStr Tff7b72= Tff7b72if Tb4b4b4(Te6edf3cotTypeEnum Tff7b72=Tff7b72= Te6edf3CotTypeTb4b4b4.Te6edf3CotType_OtherTb4b4b4) Te6edf3type Tff7b72else Ta5d6ff"Ta5d6ff"
Tff7b72val Te6edf3howEnum Tff7b72= Te6edf3TakV2TypeMapperTb4b4b4.Te6edf3cotHowFromStringTb4b4b4(Te6edf3howTb4b4b4)

Tff7b72val Te6edf3teamEnum Tff7b72=
Te6edf3groupTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3TeamTb4b4b4.Te6edf3fromValueTb4b4b4(Te6edf3TakConversionHelpersTb4b4b4.Te6edf3getTeamValueTb4b4b4(Tffa657itTb4b4b4.Te6edf3nameTb4b4b4)Tb4b4b4) Tb4b4b4} Tff7b72?: Te6edf3TeamTb4b4b4.Te6edf3Unspecifed_Color

Tff7b72val Te6edf3roleEnum Tff7b72=
Te6edf3groupTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3MemberRoleTb4b4b4.Te6edf3fromValueTb4b4b4(Te6edf3TakConversionHelpersTb4b4b4.Te6edf3getMemberRoleValueTb4b4b4(Tffa657itTb4b4b4.Te6edf3roleTb4b4b4)Tb4b4b4) Tb4b4b4}
Tff7b72?: Te6edf3MemberRoleTb4b4b4.Te6edf3Unspecifed

Tff7b72val Te6edf3battery Tff7b72= Te6edf3statusTff7b72?.Te6edf3batteryTff7b72?.Te6edf3coerceAtLeastTb4b4b4(T79c0ff0Tb4b4b4) Tff7b72?: T79c0ff0

T8b949e// PLI (position reports): match all atom CoT types ("a-*").
T8b949e// The original type is preserved via cot_type_id/cot_type_str so that hostile
T8b949e// (a-h-*), neutral (a-n-*), and unknown (a-u-*) markers round-trip correctly.
T8b949e// toCoTMessage() restores the exact type from those fields instead of defaulting
T8b949e// to DEFAULT_PLI_COT_TYPE, so all atom markers are treated as PLI on the wire.
Tff7b72if Tb4b4b4(Te6edf3typeTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"Ta5d6ffa-Ta5d6ff"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3callsign Tff7b72= Te6edf3contactTff7b72?.Te6edf3callsign Tff7b72?: Ta5d6ff"Ta5d6ffUNKNOWNTa5d6ff"
Tff7b72val Te6edf3deviceCallsign Tff7b72= Te6edf3uid

Tff7b72return Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3cotTypeEnumTb4b4b4,
Te6edf3cot_type_str Tff7b72= Te6edf3cotTypeStrTb4b4b4,
Te6edf3how Tff7b72= Te6edf3howEnumTb4b4b4,
Te6edf3callsign Tff7b72= Te6edf3callsignTb4b4b4,
Te6edf3device_callsign Tff7b72= Te6edf3deviceCallsignTb4b4b4,
Te6edf3uid Tff7b72= Te6edf3uidTb4b4b4,
Te6edf3team Tff7b72= Te6edf3teamEnumTb4b4b4,
Te6edf3role Tff7b72= Te6edf3roleEnumTb4b4b4,
Te6edf3latitude_i Tff7b72= Tb4b4b4(Te6edf3latitude Tff7b72* Te6edf3TAK_COORDINATE_SCALETb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3longitude_i Tff7b72= Tb4b4b4(Te6edf3longitude Tff7b72* Te6edf3TAK_COORDINATE_SCALETb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3altitude Tff7b72= Tff7b72if Tb4b4b4(Te6edf3hae Tff7b72>Tff7b72= Te6edf3TAK_UNKNOWN_POINT_VALUE Tff7b72|Tff7b72| Te6edf3haeTb4b4b4.Te6edf3isNaNTb4b4b4(Tb4b4b4)Tb4b4b4) T79c0ff0 Tff7b72else Te6edf3haeTb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)Tb4b4b4,
T8b949e// V2 encodes speed as cm/s (m/s × 100) and course as deg×100.
T8b949e// V1 (legacy TAKPacket, port 72) uses raw integers with no scaling.
T8b949e// These two paths are ALWAYS separate (different portnums) and must
T8b949e// never cross-feed: a V1 packet decoded in TAKMeshIntegration goes
T8b949e// through convertV1ToCoT() → CoTMessage.pli() → toXml(), NOT through
T8b949e// toTAKPacketV2(). If this invariant is ever broken, speed/course
T8b949e// would be silently off by ×100.
Te6edf3speed Tff7b72= Tb4b4b4(Te6edf3trackTff7b72?.Te6edf3speedTff7b72?.Te6edf3coerceAtLeastTb4b4b4(T79c0ff0.0Tb4b4b4)Tff7b72?.Te6edf3timesTb4b4b4(T79c0ff1T79c0ff0T79c0ff0Tb4b4b4)Tb4b4b4)Tff7b72?.Te6edf3toIntTb4b4b4(Tb4b4b4) Tff7b72?: T79c0ff0Tb4b4b4, T8b949e// m/s -> cm/s
Te6edf3course Tff7b72= Tb4b4b4(Te6edf3trackTff7b72?.Te6edf3courseTff7b72?.Te6edf3coerceAtLeastTb4b4b4(T79c0ff0.0Tb4b4b4)Tff7b72?.Te6edf3timesTb4b4b4(T79c0ff1T79c0ff0T79c0ff0Tb4b4b4)Tb4b4b4)Tff7b72?.Te6edf3toIntTb4b4b4(Tb4b4b4) Tff7b72?: T79c0ff0Tb4b4b4, T8b949e// deg -> deg*100
Te6edf3battery Tff7b72= Te6edf3batteryTb4b4b4,
Te6edf3geo_src Tff7b72= Te6edf3GeoPointSourceTb4b4b4.Te6edf3GeoPointSource_GPSTb4b4b4,
Te6edf3alt_src Tff7b72= Te6edf3GeoPointSourceTb4b4b4.Te6edf3GeoPointSource_GPSTb4b4b4,
T8b949e// v0.4.0: PLI is implicit — no payload_variant is set (the bool pli
T8b949e// oneof arm was removed). An a-f-* packet with no variant IS a PLI.
Tb4b4b4)
Tb4b4b4}

T8b949e// GeoChat
Tff7b72if Tb4b4b4(Te6edf3type Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffb-t-fTa5d6ff"Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3localChat Tff7b72= Te6edf3chat Tff7b72?: Tff7b72return Tff7b72null
T8b949e// ATAK GeoChat events often omit <contact callsign="..."/> — the
T8b949e// sender identity is only in <__chat senderCallsign="..."/>.
Tff7b72val Te6edf3callsign Tff7b72= Te6edf3contactTff7b72?.Te6edf3callsign Tff7b72?: Te6edf3localChatTb4b4b4.Te6edf3senderCallsign Tff7b72?: Ta5d6ff"Ta5d6ffUNKNOWNTa5d6ff"
Tff7b72val Te6edf3actualDeviceUid Tff7b72= Te6edf3uidTb4b4b4.Te6edf3geoChatSenderUidTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3messageId Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3uidTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"Ta5d6ffGeoChat.Ta5d6ff"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3uidTb4b4b4.Te6edf3geoChatMessageIdTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3RandomTb4b4b4.Te6edf3nextIntTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3toStringTb4b4b4(Te6edf3TAK_HEX_RADIXTb4b4b4)
Tb4b4b4}

Tff7b72val Te6edf3smuggledCallsign Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3actualDeviceUidTb4b4b4.Te6edf3isNotEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Ta5d6ff"Tffd700$Te6edf3actualDeviceUidTa5d6ff|Tffd700$Te6edf3messageIdTa5d6ff"
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3contactTff7b72?.Te6edf3endpoint Tff7b72?: Ta5d6ff"Ta5d6ff"
Tb4b4b4}

Tff7b72var Te6edf3toUidTb4b4b4: Tffa657String? Tff7b72= Tff7b72null
Tff7b72var Te6edf3toCallsignTb4b4b4: Tffa657String? Tff7b72= Tff7b72null
Tff7b72if Tb4b4b4(Te6edf3localChatTb4b4b4.Te6edf3chatroom Tff7b72!Tff7b72= Ta5d6ff"Ta5d6ffAll Chat RoomsTa5d6ff"Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3localChatTb4b4b4.Te6edf3chatroomTb4b4b4.Te6edf3startsWithTb4b4b4(Te6edf3uidTb4b4b4) Tff7b72|Tff7b72| Te6edf3uidTb4b4b4.Te6edf3startsWithTb4b4b4(Ta5d6ff"Ta5d6ffGeoChatTa5d6ff"Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3parts Tff7b72= Te6edf3uidTb4b4b4.Te6edf3splitTb4b4b4(Ta5d6ff"Ta5d6ff.Ta5d6ff"Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3partsTb4b4b4.Te6edf3size Tff7b72>Tff7b72= Te6edf3TAK_DIRECT_MESSAGE_PARTS_MIN Tff7b72&Tff7b72& Te6edf3partsTff7b72[T79c0ff0Tff7b72] Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffGeoChatTa5d6ff"Tb4b4b4) Tb4b4b4{
Te6edf3toUid Tff7b72= Te6edf3localChatTb4b4b4.Te6edf3chatroom
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3toCallsign Tff7b72= Te6edf3localChatTb4b4b4.Te6edf3chatroom
Tb4b4b4}
Tb4b4b4}

Tff7b72return Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3CotTypeTb4b4b4.Te6edf3CotType_b_t_fTb4b4b4,
Te6edf3how Tff7b72= Te6edf3CotHowTb4b4b4.Te6edf3CotHow_h_g_i_g_oTb4b4b4,
Te6edf3callsign Tff7b72= Te6edf3callsignTb4b4b4,
Te6edf3device_callsign Tff7b72= Te6edf3smuggledCallsignTb4b4b4,
Te6edf3uid Tff7b72= Te6edf3uidTb4b4b4,
Te6edf3team Tff7b72= Te6edf3teamEnumTb4b4b4,
Te6edf3role Tff7b72= Te6edf3roleEnumTb4b4b4,
Te6edf3battery Tff7b72= Te6edf3batteryTb4b4b4,
Te6edf3chat Tff7b72=
Te6edf3GeoChatTb4b4b4(
Te6edf3message Tff7b72= Te6edf3localChatTb4b4b4.Te6edf3messageTb4b4b4,
Te6edf3to Tff7b72= Te6edf3toUid Tff7b72?: Tff7b72if Tb4b4b4(Te6edf3toCallsign Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Ta5d6ff"Ta5d6ffAll Chat RoomsTa5d6ff" Tff7b72else Tff7b72nullTb4b4b4,
Te6edf3to_callsign Tff7b72= Te6edf3toCallsignTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

T8b949e// Fallback: wrap the whole detail XML in raw_detail for unmapped types
T8b949e// (user-drawn shapes like u-d-c-c, markers like b-m-*, alerts, etc.)
Tff7b72val Te6edf3detailBytes Tff7b72= Te6edf3parsedDetailXmlTff7b72?.Te6edf3encodeToByteArrayTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3detailBytes Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3callsign Tff7b72= Te6edf3contactTff7b72?.Te6edf3callsign Tff7b72?: Ta5d6ff"Ta5d6ffUNKNOWNTa5d6ff"
Tff7b72return Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3cotTypeEnumTb4b4b4,
Te6edf3cot_type_str Tff7b72= Te6edf3cotTypeStrTb4b4b4,
Te6edf3how Tff7b72= Te6edf3howEnumTb4b4b4,
Te6edf3callsign Tff7b72= Te6edf3callsignTb4b4b4,
Te6edf3device_callsign Tff7b72= Te6edf3uidTb4b4b4,
Te6edf3uid Tff7b72= Te6edf3uidTb4b4b4,
Te6edf3team Tff7b72= Te6edf3teamEnumTb4b4b4,
Te6edf3role Tff7b72= Te6edf3roleEnumTb4b4b4,
Te6edf3latitude_i Tff7b72= Tb4b4b4(Te6edf3latitude Tff7b72* Te6edf3TAK_COORDINATE_SCALETb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3longitude_i Tff7b72= Tb4b4b4(Te6edf3longitude Tff7b72* Te6edf3TAK_COORDINATE_SCALETb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3altitude Tff7b72= Tff7b72if Tb4b4b4(Te6edf3hae Tff7b72>Tff7b72= Te6edf3TAK_UNKNOWN_POINT_VALUE Tff7b72|Tff7b72| Te6edf3haeTb4b4b4.Te6edf3isNaNTb4b4b4(Tb4b4b4)Tb4b4b4) T79c0ff0 Tff7b72else Te6edf3haeTb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3battery Tff7b72= Te6edf3batteryTb4b4b4,
Te6edf3raw_detail Tff7b72= Te6edf3detailBytesTb4b4b4.Te6edf3toByteStringTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffCannot convert CoT to TAKPacketV2 for type Tffd700$Te6edf3typeTa5d6ff (no parsed detail)Ta5d6ff" Tb4b4b4}
Tff7b72return Tff7b72null
Tb4b4b4}

Tff7b72fun Te6edf3TAKPacketV2Tb4b4b4.Td2a8fftoCoTMessageTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3CoTMessage? Tb4b4b4{
Tff7b72val Te6edf3senderCallsign Tff7b72= Te6edf3callsignTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Ta5d6ff"Ta5d6ffUNKNOWNTa5d6ff" Tb4b4b4}
Tff7b72val Te6edf3rawDeviceCallsign Tff7b72= Te6edf3device_callsignTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Te6edf3uidTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Ta5d6ff"Ta5d6ffUNKNOWNTa5d6ff" Tb4b4b4} Tb4b4b4}
Tff7b72val Te6edf3timeNow Tff7b72= Te6edf3ClockTb4b4b4.Te6edf3SystemTb4b4b4.Te6edf3nowTb4b4b4(Tb4b4b4)
Tff7b72val Tb4b4b4(Te6edf3senderUidTb4b4b4, Te6edf3messageIdTb4b4b4) Tff7b72= Te6edf3TakConversionHelpersTb4b4b4.Te6edf3parseDeviceCallsignTb4b4b4(Te6edf3rawDeviceCallsignTb4b4b4)

T8b949e// PLI — v0.4.0: implicit (the `bool pli` oneof arm was removed). A packet
T8b949e// with NO typed payload_variant arm set is a position report. Every typed
T8b949e// arm must be excluded so a shape/marker/route/etc. isn't mis-rendered as a
T8b949e// PLI dot if it ever reaches this fallback path.
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3hasTypedPayloadTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3staleMinutes Tff7b72= Tff7b72if Tb4b4b4(Te6edf3stale_seconds Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4(Te6edf3stale_seconds Tff7b72/ T79c0ff6T79c0ff0Tb4b4b4) Tff7b72else Te6edf3DEFAULT_TAK_STALE_MINUTES
T8b949e// Restore the original CoT type and how from the packet — pli() defaults to
T8b949e// DEFAULT_PLI_COT_TYPE/"m-g" but the sending node may have been hostile (a-h-*),
T8b949e// neutral (a-n-*), unknown (a-u-*), etc.
Tff7b72val Te6edf3resolvedType Tff7b72=
Te6edf3cot_type_strTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Te6edf3TakV2TypeMapperTb4b4b4.Te6edf3cotTypeToStringTb4b4b4(Te6edf3cot_type_idTb4b4b4) Tff7b72?: Te6edf3DEFAULT_PLI_COT_TYPE Tb4b4b4}
Tff7b72val Te6edf3resolvedHow Tff7b72= Te6edf3TakV2TypeMapperTb4b4b4.Te6edf3cotHowToStringTb4b4b4(Te6edf3howTb4b4b4) Tff7b72?: Ta5d6ff"Ta5d6ffm-gTa5d6ff"
Tff7b72return Te6edf3CoTMessageTb4b4b4.Te6edf3pliTb4b4b4(
Te6edf3uid Tff7b72= Te6edf3senderUidTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Te6edf3uid Tb4b4b4}Tb4b4b4,
Te6edf3callsign Tff7b72= Te6edf3senderCallsignTb4b4b4,
Te6edf3latitude Tff7b72= Te6edf3latitude_iTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ Te6edf3TAK_COORDINATE_SCALETb4b4b4,
Te6edf3longitude Tff7b72= Te6edf3longitude_iTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ Te6edf3TAK_COORDINATE_SCALETb4b4b4,
Te6edf3altitude Tff7b72= Te6edf3altitudeTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3speed Tff7b72= Te6edf3speedTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ T79c0ff1T79c0ff0T79c0ff0.0Tb4b4b4, T8b949e// cm/s -> m/s
Te6edf3course Tff7b72= Te6edf3courseTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ T79c0ff1T79c0ff0T79c0ff0.0Tb4b4b4, T8b949e// deg*100 -> deg
Te6edf3team Tff7b72= Te6edf3TakConversionHelpersTb4b4b4.Te6edf3teamToColorNameTb4b4b4(Te6edf3teamTb4b4b4)Tb4b4b4,
Te6edf3role Tff7b72= Te6edf3TakConversionHelpersTb4b4b4.Te6edf3roleToNameTb4b4b4(Te6edf3roleTb4b4b4)Tb4b4b4,
Te6edf3battery Tff7b72= Te6edf3batteryTb4b4b4,
Te6edf3staleMinutes Tff7b72= Te6edf3staleMinutesTb4b4b4,
Tb4b4b4)
Tb4b4b4.Te6edf3copyTb4b4b4(Te6edf3type Tff7b72= Te6edf3resolvedTypeTb4b4b4, Te6edf3how Tff7b72= Te6edf3resolvedHowTb4b4b4)
Tb4b4b4}

T8b949e// GeoChat
Tff7b72val Te6edf3localChat Tff7b72= Te6edf3chat
Tff7b72if Tb4b4b4(Te6edf3localChat Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
T8b949e// chat.to carries the recipient/room ID for DMs; null means broadcast.
T8b949e// Do NOT fall through to chat.to_callsign here — despite the name,
T8b949e// it holds the SENDER's callsign (the parser stores __chat[@senderCallsign]
T8b949e// there), not a chatroom name.
Tff7b72val Te6edf3chatroom Tff7b72= Te6edf3localChatTb4b4b4.Te6edf3to Tff7b72?: Ta5d6ff"Ta5d6ffAll Chat RoomsTa5d6ff"

Tff7b72val Te6edf3msgId Tff7b72= Te6edf3messageId Tff7b72?: Te6edf3RandomTb4b4b4.Te6edf3nextIntTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3toStringTb4b4b4(Te6edf3TAK_HEX_RADIXTb4b4b4)
Tff7b72val Te6edf3staleTime Tff7b72=
Te6edf3timeNow Tff7b72+
Tff7b72if Tb4b4b4(Te6edf3stale_seconds Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3stale_secondsTb4b4b4.Te6edf3seconds
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3DEFAULT_TAK_STALE_MINUTESTb4b4b4.Te6edf3minutes
Tb4b4b4}

Tff7b72return Te6edf3CoTMessageTb4b4b4(
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffGeoChat.Tffd700$Te6edf3senderUidTa5d6ff.Tffd700$Te6edf3chatroomTa5d6ff.Tffd700$Te6edf3msgIdTa5d6ff"Tb4b4b4,
Te6edf3type Tff7b72= Ta5d6ff"Ta5d6ffb-t-fTa5d6ff"Tb4b4b4,
Te6edf3how Tff7b72= Ta5d6ff"Ta5d6ffh-g-i-g-oTa5d6ff"Tb4b4b4,
Te6edf3time Tff7b72= Te6edf3timeNowTb4b4b4,
Te6edf3start Tff7b72= Te6edf3timeNowTb4b4b4,
Te6edf3stale Tff7b72= Te6edf3staleTimeTb4b4b4,
Te6edf3latitude Tff7b72= Te6edf3latitude_iTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ Te6edf3TAK_COORDINATE_SCALETb4b4b4,
Te6edf3longitude Tff7b72= Te6edf3longitude_iTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ Te6edf3TAK_COORDINATE_SCALETb4b4b4,
Te6edf3contact Tff7b72= Te6edf3CoTContactTb4b4b4(Te6edf3callsign Tff7b72= Te6edf3senderCallsignTb4b4b4, Te6edf3endpoint Tff7b72= Te6edf3DEFAULT_TAK_ENDPOINTTb4b4b4)Tb4b4b4,
Te6edf3group Tff7b72=
Te6edf3CoTGroupTb4b4b4(
Te6edf3name Tff7b72= Te6edf3TakConversionHelpersTb4b4b4.Te6edf3teamToColorNameTb4b4b4(Te6edf3teamTb4b4b4)Tb4b4b4,
Te6edf3role Tff7b72= Te6edf3TakConversionHelpersTb4b4b4.Te6edf3roleToNameTb4b4b4(Te6edf3roleTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Te6edf3status Tff7b72= Te6edf3CoTStatusTb4b4b4(Te6edf3battery Tff7b72= Te6edf3batteryTb4b4b4)Tb4b4b4,
Te6edf3chat Tff7b72= Te6edf3CoTChatTb4b4b4(Te6edf3chatroom Tff7b72= Te6edf3chatroomTb4b4b4, Te6edf3senderCallsign Tff7b72= Te6edf3senderCallsignTb4b4b4, Te6edf3message Tff7b72= Te6edf3localChatTb4b4b4.Te6edf3messageTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

T8b949e// Raw detail: unmapped CoT types round-tripped as opaque detail bytes.
T8b949e// Emit a bare CoTMessage whose <detail> is the raw bytes verbatim. Do NOT populate
T8b949e// contact/group/status here — those would be double-emitted by toXml() alongside
T8b949e// rawDetailXml, corrupting the CoT stream.
Tff7b72val Te6edf3rawDetail Tff7b72= Te6edf3raw_detail
Tff7b72if Tb4b4b4(Te6edf3rawDetail Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3rawXml Tff7b72= Te6edf3rawDetailTb4b4b4.Te6edf3utf8Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3resolvedType Tff7b72=
Te6edf3cot_type_strTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Te6edf3TakV2TypeMapperTb4b4b4.Te6edf3cotTypeToStringTb4b4b4(Te6edf3cot_type_idTb4b4b4) Tff7b72?: Te6edf3DEFAULT_PLI_COT_TYPE Tb4b4b4}
Tff7b72val Te6edf3resolvedHow Tff7b72= Te6edf3TakV2TypeMapperTb4b4b4.Te6edf3cotHowToStringTb4b4b4(Te6edf3howTb4b4b4) Tff7b72?: Ta5d6ff"Ta5d6ffm-gTa5d6ff"
Tff7b72val Te6edf3staleTime Tff7b72=
Te6edf3timeNow Tff7b72+
Tff7b72if Tb4b4b4(Te6edf3stale_seconds Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{
Te6edf3stale_secondsTb4b4b4.Te6edf3seconds
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3DEFAULT_TAK_STALE_MINUTESTb4b4b4.Te6edf3minutes
Tb4b4b4}
Tff7b72return Te6edf3CoTMessageTb4b4b4(
Te6edf3uid Tff7b72= Te6edf3uidTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Te6edf3senderUidTb4b4b4.Te6edf3ifEmpty Tb4b4b4{ Ta5d6ff"Ta5d6fftak-rawTa5d6ff" Tb4b4b4} Tb4b4b4}Tb4b4b4,
Te6edf3type Tff7b72= Te6edf3resolvedTypeTb4b4b4,
Te6edf3how Tff7b72= Te6edf3resolvedHowTb4b4b4,
Te6edf3time Tff7b72= Te6edf3timeNowTb4b4b4,
Te6edf3start Tff7b72= Te6edf3timeNowTb4b4b4,
Te6edf3stale Tff7b72= Te6edf3staleTimeTb4b4b4,
Te6edf3latitude Tff7b72= Te6edf3latitude_iTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ Te6edf3TAK_COORDINATE_SCALETb4b4b4,
Te6edf3longitude Tff7b72= Te6edf3longitude_iTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4) Tff7b72/ Te6edf3TAK_COORDINATE_SCALETb4b4b4,
Te6edf3hae Tff7b72= Tff7b72if Tb4b4b4(Te6edf3altitude Tff7b72=Tff7b72= T79c0ff0Tb4b4b4) Te6edf3TAK_UNKNOWN_POINT_VALUE Tff7b72else Te6edf3altitudeTb4b4b4.Te6edf3toDoubleTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3rawDetailXml Tff7b72= Te6edf3rawXmlTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffCannot convert TAKPacketV2 to CoTMessage: no PLI, chat, or raw_detail payloadTa5d6ff" Tb4b4b4}
Tff7b72return Tff7b72null
Tb4b4b4}

T8b949e/** True when any typed payload_variant arm is set (i.e. NOT a bare PLI). */
Tff7b72private Tff7b72fun Te6edf3TAKPacketV2Tb4b4b4.Td2a8ffhasTypedPayloadTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3chat Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3taktalk Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3taktalk_room Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3aircraft Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3shape Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3marker Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3rab Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3route Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3casevac Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3emergency Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3task Tff7b72!Tff7b72= Tff7b72null Tff7b72|Tff7b72|
Te6edf3raw_detail Tff7b72!Tff7b72= Tff7b72null
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.2s